package builtin

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"pwsh-go/internal/powershell"
)

// ToolCall represents a tool call request from the AI
type ToolCall struct {
	Name      string                 `json:"name"`
	Arguments map[string]interface{} `json:"arguments"`
}

// ToolResult represents the result of a tool execution
type ToolResult struct {
	Success bool        `json:"success"`
	Output  string      `json:"output"`
	Error   string      `json:"error,omitempty"`
	Data    interface{} `json:"data,omitempty"`
}

// ToolRegistry manages available tools for the agent
type ToolRegistry struct {
	tools   map[string]Tool
	backend powershell.Backend
}

// Tool interface for agent tools
type Tool interface {
	Name() string
	Description() string
	Execute(args map[string]interface{}) ToolResult
}

// NewToolRegistry creates a new tool registry
func NewToolRegistry(backend powershell.Backend) *ToolRegistry {
	registry := &ToolRegistry{
		tools:   make(map[string]Tool),
		backend: backend,
	}

	// Register built-in tools
	registry.RegisterTool(&ReadFileTool{})
	registry.RegisterTool(&WriteFileTool{})
	registry.RegisterTool(&ListDirectoryTool{})
	registry.RegisterTool(&ExecuteCommandTool{backend: backend})
	registry.RegisterTool(&SearchFilesTool{})
	registry.RegisterTool(&GetWorkingDirectoryTool{})
	registry.RegisterTool(&ChangeDirectoryTool{backend: backend})

	return registry
}

// RegisterTool registers a new tool
func (r *ToolRegistry) RegisterTool(tool Tool) {
	r.tools[tool.Name()] = tool
}

// GetTool retrieves a tool by name
func (r *ToolRegistry) GetTool(name string) (Tool, bool) {
	tool, exists := r.tools[name]
	return tool, exists
}

// ListTools returns all available tools
func (r *ToolRegistry) ListTools() []Tool {
	tools := make([]Tool, 0, len(r.tools))
	for _, tool := range r.tools {
		tools = append(tools, tool)
	}
	return tools
}

// ExecuteTool executes a tool by name with given arguments
func (r *ToolRegistry) ExecuteTool(name string, args map[string]interface{}) ToolResult {
	tool, exists := r.GetTool(name)
	if !exists {
		return ToolResult{
			Success: false,
			Error:   fmt.Sprintf("Tool '%s' not found", name),
		}
	}

	return tool.Execute(args)
}

// ReadFileTool reads file contents
type ReadFileTool struct{}

func (t *ReadFileTool) Name() string {
	return "read_file"
}

func (t *ReadFileTool) Description() string {
	return "Read the contents of a file. Args: {\"path\": \"file_path\"}"
}

func (t *ReadFileTool) Execute(args map[string]interface{}) ToolResult {
	path, ok := args["path"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'path' argument"}
	}

	content, err := os.ReadFile(path)
	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Failed to read file: %v", err)}
	}

	return ToolResult{Success: true, Output: string(content)}
}

// WriteFileTool writes content to a file
type WriteFileTool struct{}

func (t *WriteFileTool) Name() string {
	return "write_file"
}

func (t *WriteFileTool) Description() string {
	return "Write content to a file. Args: {\"path\": \"file_path\", \"content\": \"file_content\"}"
}

func (t *WriteFileTool) Execute(args map[string]interface{}) ToolResult {
	path, ok := args["path"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'path' argument"}
	}

	content, ok := args["content"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'content' argument"}
	}

	// Create directory if it doesn't exist
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Failed to create directory: %v", err)}
	}

	err := os.WriteFile(path, []byte(content), 0644)
	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Failed to write file: %v", err)}
	}

	return ToolResult{Success: true, Output: fmt.Sprintf("Successfully wrote %d bytes to %s", len(content), path)}
}

// ListDirectoryTool lists directory contents
type ListDirectoryTool struct{}

func (t *ListDirectoryTool) Name() string {
	return "list_directory"
}

func (t *ListDirectoryTool) Description() string {
	return "List contents of a directory. Args: {\"path\": \"directory_path\"}"
}

func (t *ListDirectoryTool) Execute(args map[string]interface{}) ToolResult {
	path, ok := args["path"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'path' argument"}
	}

	entries, err := os.ReadDir(path)
	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Failed to read directory: %v", err)}
	}

	var result []string
	for _, entry := range entries {
		if entry.IsDir() {
			result = append(result, entry.Name()+"/")
		} else {
			result = append(result, entry.Name())
		}
	}

	return ToolResult{Success: true, Output: strings.Join(result, "\n"), Data: result}
}

// ExecuteCommandTool executes PowerShell commands
type ExecuteCommandTool struct {
	backend powershell.Backend
}

func (t *ExecuteCommandTool) Name() string {
	return "execute_command"
}

func (t *ExecuteCommandTool) Description() string {
	return "Execute a PowerShell command. Args: {\"command\": \"powershell_command\"}"
}

func (t *ExecuteCommandTool) Execute(args map[string]interface{}) ToolResult {
	cmd, ok := args["command"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'command' argument"}
	}

	output, err := t.backend.Execute(cmd)
	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Command failed: %v", err), Output: output}
	}

	return ToolResult{Success: true, Output: output}
}

// SearchFilesTool searches for files matching a pattern
type SearchFilesTool struct{}

func (t *SearchFilesTool) Name() string {
	return "search_files"
}

func (t *SearchFilesTool) Description() string {
	return "Search for files matching a pattern. Args: {\"pattern\": \"search_pattern\", \"path\": \"search_path\"}"
}

func (t *SearchFilesTool) Execute(args map[string]interface{}) ToolResult {
	pattern, ok := args["pattern"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'pattern' argument"}
	}

	searchPath, ok := args["path"].(string)
	if !ok {
		searchPath = "."
	}

	var matches []string
	err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil // Continue walking
		}

		matched, err := filepath.Match(pattern, filepath.Base(path))
		if err != nil {
			return nil
		}

		if matched {
			matches = append(matches, path)
		}

		return nil
	})

	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Search failed: %v", err)}
	}

	return ToolResult{Success: true, Output: strings.Join(matches, "\n"), Data: matches}
}

// GetWorkingDirectoryTool gets current working directory
type GetWorkingDirectoryTool struct{}

func (t *GetWorkingDirectoryTool) Name() string {
	return "get_working_directory"
}

func (t *GetWorkingDirectoryTool) Description() string {
	return "Get the current working directory. No arguments required."
}

func (t *GetWorkingDirectoryTool) Execute(args map[string]interface{}) ToolResult {
	wd, err := os.Getwd()
	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Failed to get working directory: %v", err)}
	}

	return ToolResult{Success: true, Output: wd}
}

// ChangeDirectoryTool changes working directory
type ChangeDirectoryTool struct {
	backend powershell.Backend
}

func (t *ChangeDirectoryTool) Name() string {
	return "change_directory"
}

func (t *ChangeDirectoryTool) Description() string {
	return "Change the current working directory. Args: {\"path\": \"directory_path\"}"
}

func (t *ChangeDirectoryTool) Execute(args map[string]interface{}) ToolResult {
	path, ok := args["path"].(string)
	if !ok {
		return ToolResult{Success: false, Error: "Missing or invalid 'path' argument"}
	}

	// Use PowerShell to change directory
	cmd := fmt.Sprintf("Set-Location '%s'; Get-Location", path)
	output, err := t.backend.Execute(cmd)
	if err != nil {
		return ToolResult{Success: false, Error: fmt.Sprintf("Failed to change directory: %v", err)}
	}

	return ToolResult{Success: true, Output: strings.TrimSpace(output)}
}

// ParseToolCalls parses tool calls from AI response
func ParseToolCalls(response string) ([]ToolCall, error) {
	var toolCalls []ToolCall

	// Look for JSON tool call patterns in the response
	// This is a simplified parser - in practice, you'd want more robust parsing
	lines := strings.Split(response, "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if strings.HasPrefix(line, "{") && strings.HasSuffix(line, "}") {
			var toolCall ToolCall
			if err := json.Unmarshal([]byte(line), &toolCall); err == nil {
				if toolCall.Name != "" {
					toolCalls = append(toolCalls, toolCall)
				}
			}
		}
	}

	return toolCalls, nil
}